﻿
#include "asyndns.h"
#include "iodgram.h"
#include <tchar.h>
#pragma comment(lib, "IPHLPAPI.lib")

#define DNS_PORT        53


// sizeof(dnshdr_t) = 12
typedef struct __DNSHEADER {
    unsigned short    transaction_id;//标识

    //u1

    //RD: 递归查询标志，1表执行递归查询：若目标DNS服务器无法解析某个主机名，则它将向其他DNS服务器继续查询，如此递归，
    //直至获得结果并返回给客户端；0表示迭代查询：若目标DNS服务器无法解析某个主机名，
    //则将它自己知道的其他DNS服务器的IP地址返回给客户端，让客户端决定是否继续往向其他DNS发送请求。
    unsigned char     rd : 1;   // recursion desired

    //TC: 截断标志，仅被应答报文使用。因为UDP数据报是有长度限制的，过长的DNS报文将被截断。为1表DNS报文超过512字节并被截断
    unsigned char     tc : 1;   // truncated

    //AA: 授权应答标志，仅被应答报文使用。1表示该DNS服务商是授权服务器
    unsigned char     aa : 1;   // authoritative

    //opcode: 定义查询/应答的类型，0表标准查询，1表反向查询(由IP地址获得主机域名)，2表请求服务器状态，3-15保留；
    unsigned char     opcode : 4;

#define DNS_QUERY       0
#define DNS_RESPONSE    1
    unsigned char     qr : 1;   // 请求还是应答

    //u2
    //rcode: 4位返回码，表应答状态。0表无错误，3表域名不存在
    /*
    对于查询报而言：
    (3) 16位问题个数: 一般包含一个查询问题
    (4) 16位应答资源个数、16位授权资源记录数目、16位额外资源记录数据: 查询报中至少为1；

    对于应答报而言：
    (3) 应答报文的应答资源记录数：至少为1，
    (4) 授权资源、额外资源记录数据：可为0或非0

    (5) 查询问题的格式为：
    */

    /*
    0：No error condition，没有错误条件；
    1：Format error，请求格式有误，服务器无法解析请求；
    2：Server failure，服务器出错。
    3：Name Error，只在权威DNS服务器的响应中有意义，表示请求中的域名不存在。
    4：Not Implemented，服务器不支持该请求类型。
    5：Refused，服务器拒绝执行请求操作。
    6~15：保留备用。
    */
    unsigned char     rcode : 4;
    //以下三位未使用,必须设置为0
    unsigned char     cd : 1;   // checking disable
    unsigned char     ad : 1;   // authenticated data
    unsigned char     res : 1;  // reserved

    //RA：允许递归标志，仅被应答报文使用。1表示DNS支持服务器递归查询 
    unsigned char     ra : 1;   // recursion available


    //问题数（2字节）："问题"域中的问题的数量。
    unsigned short    nquestion;

    //资源记录数（2字节）："回答"域中的RR的数量。
    unsigned short    nanswer;

    //授权资源数（2字节）："授权"域中的RR的数量。
    unsigned short    nauthority;

    //附加资源数（2字节）："附件信息"域中的RR的数量。
    unsigned short    naddtional;
} DNSHEADER;

//资源记录（Resource Records，RRs）
typedef struct __DNSRR {
    union
    {
#define DNS_NAME_MAXLEN 260  //256 + 4
        char        name[DNS_NAME_MAXLEN]; // original domain, such as www.example.com

        struct  in_addr  addr;
    };


    // ipv4
#define DNS_TYPE_A      1   //则name是主机名，value是其对应的ip；
#define DNS_TYPE_NS     2   //则name是一个域，value是一个权威DNS服务器的主机名。该记录表示name域的域名解析将由value主机名对应的DNS服务器来做；
#define DNS_TYPE_CNAME  5   //则value是别名为name的主机对应的规范主机名；
#define DNS_TYPE_SOA    6   
#define DNS_TYPE_WKS    11
#define DNS_TYPE_PTR    12
#define DNS_TYPE_HINFO  13  //域名信息
#define DNS_TYPE_MX     15  //则value是别名为name的邮件服务器的规范主机名；
#define DNS_TYPE_TXT    16  //这是一个字符串

#define DNS_TYPE_AAAA   28  // ipv6
#define DNS_TYPE_AXFR   252
#define DNS_TYPE_ANY    255
    unsigned short    rtype;

#define DNS_CLASS_IN    1
    unsigned short    rclass;
    unsigned int    ttl;//生存时间
    unsigned short    datalen;//data所占的字节数
} DNSRR;

typedef struct __DNSSTRUCT{
    DNSHEADER    hdr;
    DNSRR**      questions;
    DNSRR**      answers;
    DNSRR**      authorities;
    DNSRR**      addtionals;
} DNSSTRUCT;

void dnsio_read_cb(struct __IODGRAM * pDgram, unsigned int uSize,
    const SOCKADDR_IN * pRemote, void * pUserData);
void dnsio_write_cb(struct __IODGRAM * pDgram, unsigned int uSize,
    const SOCKADDR_IN * pRemote, void * pUserData);
void dnsio_signal_cb(struct __IODGRAM * pDgram, DWORD dwEvent, DWORD dwError);


static int dnsio_name_encode(const char* strHost, unsigned char* pBuffer, int nSize);
static int dnsio_name_decode(const char* pBuffer, char* strHost, int nSize);
static int dnsio_records_decode(const char * pBuffer, int nSize, DNSRR * pDnsrr);
static BOOL dnsio_pack_encode(IODGRAM * pDgram, LPCTSTR strHost);
static DNSSTRUCT * dnsio_pack_decode(IODGRAM * pDgram);
static DNSSTRUCT * dnsio_new_dnsstruct(DNSHEADER * pDNSHeader);
static void dnsio_free_dnsstruct(DNSSTRUCT * pDnsStruct);

static int dnsio_init_serverinfo(DNSIO * pDns);
static BOOL dnsio_get_nextserver(DNSIO * pDns, SOCKADDR_IN * pServer);
static BOOL dnsio_send_query(DNSIO * pDns);
static BOOL dnsio_read(DNSIO * pDns);


DNSIO * dnsio_new(HIOCPBASE hIocp)
{
    BOOL bOK = FALSE;
    DNSIO * pDns = NULL;

    do
    {
        pDns = (DNSIO*)IOCP_MALLOC(sizeof(DNSIO));
        memset(pDns, 0, sizeof(DNSIO));

        pDns->hObject = fileio_create(hIocp, 0, IOTYPE_UDP);
        if (NULL == pDns->hObject) break;

        pDns->pInput = iodgram_new(pDns->hObject, 4096, pDns);
        pDns->pOutput = iodgram_new(pDns->hObject, 4096, pDns);

        if (!pDns->pInput || !pDns->pOutput) break;

        fileio_set_dgramcb(pDns->hObject, dnsio_read_cb, dnsio_write_cb, dnsio_signal_cb);

        fileio_enable_read(pDns->hObject, TRUE);
        fileio_enable_write(pDns->hObject, TRUE);

        if (0 == dnsio_init_serverinfo(pDns)) break;

        bOK = TRUE;
    } while (FALSE);

    if (FALSE == bOK && pDns)
    {
        if (pDns->pInput)
        {
            iodgram_free(pDns->pInput);
        }

        if (pDns->pOutput)
        {
            iodgram_free(pDns->pInput);
        }

        if (pDns->hObject)
        {
            fileio_close(pDns->hObject, 0);
        }

        IOCP_FREE(pDns);
        pDns = NULL;
        
    }

    return pDns;
}

void dnsio_setcb(DNSIO * pDns, PFN_DNSCALLBACK pfnCallback, void * pUserData)
{
    pDns->pfnCallback = pfnCallback;
    pDns->pUserData = pUserData;
}

BOOL dnsio_lookup(DNSIO * pDns, LPCTSTR strHost, DWORD dwSecondTimeOut)
{
    BOOL bRet = FALSE;
    HIOCPFILE hObject = pDns->hObject;

    if (dwSecondTimeOut > 7 * 24 * 60 * 60) return FALSE;

    if (_tcslen(strHost) >= sizeof(pDns->strHost)) return FALSE;

    IOCP_LOCK(hObject);

    pDns->dwTimeOut = dwSecondTimeOut * 1000;
    pDns->tiBegin = iocp_get_time();
    _tcscpy_s(pDns->strHost, sizeof(pDns->strHost) / sizeof(TCHAR), strHost);
    dnsio_pack_encode(pDns->pOutput, strHost);

    dnsio_read(pDns);

    //发送请求报文
    bRet = dnsio_send_query(pDns);

    IOCP_UNLOCK(hObject);

    return bRet;
}

int dnsio_get_addrcount(DNSIO * pDns)
{
    int nCount = 0;
    HIOCPFILE hObject = pDns->hObject;
    IOCP_LOCK(hObject);

    nCount = pDns->nIpAddrCount;
    IOCP_LOCK(hObject);
    return nCount;
}

BOOL dnsio_get_addr(DNSIO * pDns, int nIndex, struct in_addr * pAddr)
{
    BOOL bRet = FALSE;
    HIOCPFILE hObject = pDns->hObject;
    IOCP_LOCK(hObject);

    if (pDns->nIpAddrCount > nIndex)
    {
        *pAddr = pDns->AddrArray[nIndex];
        bRet = TRUE;
    }

    IOCP_LOCK(hObject);
    return bRet;
}

void dnsio_free(DNSIO * pDns)
{
    if (pDns->pServerInfo)
    {
        IOCP_FREE(pDns->pServerInfo);
    }

    if (pDns->pInput)
    {
        iodgram_free(pDns->pInput);
    }

    if (pDns->pOutput)
    {
        iodgram_free(pDns->pInput);
    }

    if (pDns->hObject)
    {
        fileio_close(pDns->hObject, 0);
    }

    IOCP_FREE(pDns);
}

// www.example.com\0 => 3www7example3com\0
static int dnsio_name_encode(const char* strHost, unsigned char* pBuffer, int nSize)
{
    unsigned char uIndex = 0;//一个域名的最大长度是不会超过这个数的
    int nLenght = 0;

    //求长度
    nLenght = strlen(strHost) + 1;

    if (1 == nLenght) return 0;

    if ('.' != strHost[nLenght - 2])
    {
        //其实 “www.baidu.com.” 跟 “www.baidu.com” 是一样的
        nLenght++;//'\0'计算在内
    }

    if (nLenght > nSize)
    {
        return nLenght - nSize;
    }

    nLenght = 0;
    //求内容
    do
    {
        if ('.' == *strHost || '\0' == *strHost)
        {
            if (uIndex)
            {
                pBuffer[nLenght - (int)uIndex - 1] = uIndex;
            }
            uIndex = 0;
        }
        else
        {
            if (0 == uIndex)
            {
                nLenght++;
            }
            uIndex++;
            pBuffer[nLenght++] = *strHost;
        }

    } while (*strHost++);

    pBuffer[nLenght++] = '\0';
    return nLenght;
}

// 3www7example3com\0 => www.example.com\0
static int dnsio_name_decode(const char* pBuffer, char* strHost, int nSize)
{
    unsigned char uIndex = 0;//一个域名的最大长度是不会超过这个数的
    int nLenght = 0;

    while (pBuffer[nLenght] && 0xc0 != (0xc0 & pBuffer[nLenght]))
    {
        nLenght++;
    }

    if (nLenght > nSize)
    {
        return nLenght - nSize;
    }

    nLenght = 0;
   
    //求内容
    while (*pBuffer && 0xc0 != (0xc0 & *pBuffer))
    {
        uIndex = *pBuffer;
        pBuffer++;

        while (uIndex--)
        {
            strHost[nLenght++] = *pBuffer;
            pBuffer++;
        }

        strHost[nLenght++] = '.';
    }

    strHost[nLenght - 1] = '\0';

    return nLenght - (!!(0xc0 & *pBuffer));
}

static int dnsio_records_decode(const char * pBuffer,int nSize, DNSRR * pDnsrr)
{
    int nOffset = 0;
    int nLenght = 0;
    struct __DNSFLAG
    {
        unsigned short    rtype;
        unsigned short    rclass;
    } *pDnsFlag = NULL;

#pragma pack (1)
    struct __DNSDATA
    {
        unsigned short    roffset;
        unsigned short    rtype;
        unsigned short    rclass;
        unsigned int      ttl;//生存时间
        unsigned short    datalen;//data所占的字节数
    } *pDnsData = (struct __DNSDATA*)pBuffer;
#pragma pack ()

    if (pBuffer[0] & 0xc0)
    {
        //消息压缩格式，11000000 00000000，两个字节，前2位为跳转标志，后14位为跳转的偏移

        pDnsrr->rtype = ntohs(pDnsData->rtype);
        pDnsrr->rclass = ntohs(pDnsData->rclass);
        pDnsrr->ttl = ntohl(pDnsData->ttl);
        pDnsrr->datalen = ntohs(pDnsData->datalen);

        pBuffer += sizeof(struct __DNSDATA);
        if (DNS_TYPE_A == pDnsrr->rtype)
        {
            pDnsrr->addr = *(struct in_addr *)(pBuffer);
        }
        else if (DNS_TYPE_CNAME == pDnsrr->rtype)
        {
            nOffset = dnsio_name_decode(pBuffer, pDnsrr->name, sizeof(pDnsrr->name));
            
            if (0 >= nOffset) return -1;
        }

        nOffset = pDnsrr->datalen + sizeof(struct __DNSDATA);
    }
    else
    {
        nOffset = dnsio_name_decode(pBuffer, pDnsrr->name, sizeof(pDnsrr->name));

        if (0 >= nOffset || nOffset + (int)sizeof(struct __DNSFLAG) > nSize) return -1;

        nLenght = strlen(pBuffer) + 1;
        pDnsFlag = (struct __DNSFLAG*)&pBuffer[nLenght];
        
        pDnsrr->rtype = ntohs(pDnsFlag->rtype);
        pDnsrr->rclass = ntohs(pDnsFlag->rclass);
        pDnsrr->ttl = 0;
        pDnsrr->datalen = nOffset;

        nOffset = nLenght + sizeof(struct __DNSFLAG);
    }

    return nOffset;
}

static BOOL dnsio_pack_encode(IODGRAM * pDgram, LPCTSTR strHost)
{
    DNSHEADER DNSHeader = { 0 };
    int nEncodeSize = 0;
    char strEncode[DNS_NAME_MAXLEN] = { 0 };
    struct
    {
        unsigned short    rtype;
        unsigned short    rclass;
    }DnsFlag = { 0 };

#ifdef _UNICODE
    char strText[DNS_NAME_MAXLEN] = { 0 };
    WideCharToMultiByte(CP_ACP, 0, strHost, -1, strText, DNS_NAME_MAXLEN - 1, NULL, NULL);
#else
    const char * strText = strHost;
#endif

    nEncodeSize = dnsio_name_encode(strText, strEncode, sizeof(strEncode));

    if (0 >= nEncodeSize) return FALSE;

    //DNSHEADER
    DNSHeader.transaction_id = htons((unsigned short)GetCurrentThreadId());
    DNSHeader.qr = DNS_QUERY;
    DNSHeader.opcode = 0;
    DNSHeader.aa = 0;
    DNSHeader.tc = 0;
    DNSHeader.rd = 1;

    DNSHeader.ra = 0;
    DNSHeader.res = 0;
    DNSHeader.ad = 0;
    DNSHeader.cd = 0;
    DNSHeader.rcode = 0;

    DNSHeader.nquestion = htons(1);//问题数（2字节）："问题"域中的问题的数量。
    DNSHeader.nanswer = htons(0);//资源记录数（2字节）："回答"域中的RR的数量。
    DNSHeader.nauthority = htons(0);//授权资源数（2字节）："授权"域中的RR的数量。
    DNSHeader.naddtional = htons(0);//附加资源数（2字节）："附件信息"域中的RR的数量。

    DnsFlag.rtype = htons(DNS_TYPE_A);
    DnsFlag.rclass = htons(DNS_CLASS_IN);

    iodgram_write(pDgram, (char*)&DNSHeader, sizeof(DNSHeader));
    iodgram_write(pDgram, strEncode, nEncodeSize);
    iodgram_write(pDgram, (char*)&DnsFlag, sizeof(DnsFlag));

    return TRUE;
}

static DNSSTRUCT * dnsio_pack_decode(IODGRAM * pDgram)
{
    DNSSTRUCT * pDns = NULL;
    char Buffer[512] = { 0 };
    DNSHEADER *pDNSHeader = (DNSHEADER*)Buffer;

    int nSize = 0;
    int nCount = 0;
    int nOffset = 0;

    nSize = iodgram_peek(pDgram, (char*)pDNSHeader, 0, sizeof(DNSHEADER));

    if (sizeof(DNSHEADER) > nSize) return NULL;
    nOffset += nSize;

    pDNSHeader->transaction_id = ntohs(pDNSHeader->transaction_id);
    pDNSHeader->nquestion = ntohs(pDNSHeader->nquestion);
    pDNSHeader->nanswer = ntohs(pDNSHeader->nanswer);
    pDNSHeader->nauthority = ntohs(pDNSHeader->nauthority);
    pDNSHeader->naddtional = ntohs(pDNSHeader->naddtional);

    pDns = dnsio_new_dnsstruct(pDNSHeader);

    nCount = pDNSHeader->nquestion + pDNSHeader->nanswer +
        pDNSHeader->nauthority + pDNSHeader->naddtional;

    for (int i = 0; nCount > i; i++)
    {
        nSize = iodgram_peek(pDgram, Buffer, nOffset, sizeof(Buffer));

        if (0 >= nSize) goto FAILED;

        nSize = dnsio_records_decode(Buffer, nSize, pDns->questions[i]);

        if (0 >= nSize) goto FAILED;

        nOffset += nSize;
    }

    return pDns;

FAILED:
    dnsio_free_dnsstruct(pDns);
    return NULL;
}

static DNSSTRUCT * dnsio_new_dnsstruct(DNSHEADER * pDNSHeader)
{
    int nSize = 0;
    int nCount = 0;
    DNSSTRUCT * pDnsStruct = NULL;
    DNSRR * pPoint = NULL;

    nCount = pDNSHeader->nquestion + pDNSHeader->nanswer +
        pDNSHeader->nauthority + pDNSHeader->naddtional;

    nSize = sizeof(DNSSTRUCT) + (nCount * (sizeof(DNSRR*) + sizeof(DNSRR)));

    pDnsStruct = IOCP_MALLOC(nSize);

    if (NULL == pDnsStruct) return NULL;

    memset(pDnsStruct, 0, nSize);

    pDnsStruct->hdr = *pDNSHeader;
    pDnsStruct->questions = (DNSRR**)(((char*)pDnsStruct) + sizeof(DNSSTRUCT));
    pDnsStruct->answers = pDnsStruct->questions + pDNSHeader->nquestion;
    pDnsStruct->authorities = pDnsStruct->answers + pDNSHeader->nanswer;
    pDnsStruct->addtionals = pDnsStruct->authorities + pDNSHeader->nauthority;

    pPoint = (DNSRR*)(((char*)pDnsStruct) + sizeof(DNSSTRUCT) + nCount * (sizeof(DNSRR*)));

    for (int i = 0; nCount > i; i++)
    {
        pDnsStruct->questions[i] = &pPoint[i];
    }

    return pDnsStruct;
}

static void dnsio_free_dnsstruct(DNSSTRUCT * pDnsStruct)
{
    IOCP_FREE(pDnsStruct);
}

static int dnsio_init_serverinfo(DNSIO * pDns)
{
    // 声明变量  
    assert(NULL == pDns->pServerInfo);
    FIXED_INFO FixedInfo;
    ULONG    uBytes = sizeof(FIXED_INFO);      // 保存获取到的本地计算机网络参数信息结构体链表的长度  
    DWORD    dwError = 0;          // 调用GetNetworkParams()函数的返回值  
    IP_ADDR_STRING * pIpAddrList = NULL;   // 保存所有DNS服务器的IP地址列表  

    dwError = GetNetworkParams(&FixedInfo, &uBytes);

    if (ERROR_BUFFER_OVERFLOW == dwError)
    {
        pDns->pServerInfo = (FIXED_INFO *)IOCP_MALLOC(uBytes);

        if (GetNetworkParams(pDns->pServerInfo, &uBytes) != ERROR_SUCCESS)
        {
            IOCP_FREE(pDns->pServerInfo);
            pDns->pServerInfo = NULL;
        }
    }
    else if (ERROR_SUCCESS == dwError)
    {
        pDns->pServerInfo = (FIXED_INFO *)IOCP_MALLOC(sizeof(FIXED_INFO));
        *pDns->pServerInfo = FixedInfo;
    }
    else
    {
        return FALSE;
    }

    if (pDns->pServerInfo)
    {
        pDns->nServerCount = 1;
        pIpAddrList = pDns->pServerInfo->DnsServerList.Next;

        while (pIpAddrList)
        {
            pIpAddrList = pIpAddrList->Next;
            pDns->nServerCount++;
        }
    }

    return pDns->nServerCount;
}

static BOOL dnsio_get_nextserver(DNSIO * pDns, SOCKADDR_IN * pServer)
{
    IP_ADDRESS_STRING strIpAddr = { 0 };
    SOCKADDR_IN SockAddr = { 0 };
    IP_ADDR_STRING * pIpAddrList = NULL;
    int nIndex = 0;
    if (NULL == pDns->pServerInfo) return FALSE;

    if (pDns->nServerIndex == pDns->nServerCount)
    {
        return FALSE;
    }

    if (0 == pDns->nServerIndex)
    {
        strIpAddr = pDns->pServerInfo->DnsServerList.IpAddress;
        pDns->nServerIndex++;
    }
    else
    {
        pIpAddrList = pDns->pServerInfo->DnsServerList.Next;
        nIndex = 2;

        while (pIpAddrList && pDns->nServerCount > nIndex)
        {
            if (pDns->nServerIndex + 1 == nIndex) break;
            pIpAddrList = pIpAddrList->Next;
            nIndex++;
        }

        if (NULL == pIpAddrList) return FALSE;

        pDns->nServerIndex++;
        strIpAddr = pIpAddrList->IpAddress;
    }

    SockAddr.sin_family = AF_INET;
    SockAddr.sin_port = htons(DNS_PORT);
    SockAddr.sin_addr.s_addr = inet_addr(strIpAddr.String);

    if (0 == SockAddr.sin_addr.s_addr)
    {
        SockAddr.sin_addr.s_addr = inet_addr("114.114.114.114");
    }

    if(pServer) *pServer = SockAddr;

    return TRUE;
}

static BOOL dnsio_send_query(DNSIO * pDns)
{
    SOCKADDR_IN DnsServer = { 0 };

    if (FALSE == dnsio_get_nextserver(pDns, &DnsServer))
    {
        //IOCP_OUTPUT("1");
        return FALSE;
    }

    //DnsServer.sin_family = AF_INET;
    //DnsServer.sin_port = 53;
    //DnsServer.sin_addr.s_addr = inet_addr("114.114.114.114");

    return iodgram_output(pDns->pOutput, &DnsServer, 0);
}

static BOOL dnsio_read(DNSIO * pDns)
{
    time_t tiDiff = 0;
    DWORD dwTimeOut = 0;
    //解析失败，再次监听
    if (pDns->dwTimeOut)
    {
        //分析超时时间
        tiDiff = iocp_get_time()- pDns->tiBegin;

        if (tiDiff >= (time_t)pDns->dwTimeOut)
        {
            pDns->pfnCallback(pDns, pDns->strHost, 0, ERROR_TIMEOUT);
            return FALSE;
        }

        dwTimeOut = (DWORD)((time_t)pDns->dwTimeOut - tiDiff);
    }

    return iodgram_input(pDns->pInput, dwTimeOut);
}

void dnsio_read_cb(struct __IODGRAM * pDgram, unsigned int uSize,
    const SOCKADDR_IN * pRemote, void * pUserData)
{
    DNSRR * pDnsrr = NULL;
    DNSIO * pDns = (DNSIO *)pUserData;
    time_t tiDiff = 0;
    DWORD dwTimeOut = 0;
    DNSSTRUCT * pDnsStruct = dnsio_pack_decode(pDgram);

    if (NULL == pDnsStruct)
    {
        //解析失败，再次监听
        dnsio_read(pDns);
        return;
    }

    pDns->nIpAddrCount = 0;

    for (int i = 0; pDnsStruct->hdr.nanswer > i; i++)
    {
        pDnsrr = pDnsStruct->answers[i];

        if (DNS_TYPE_A == pDnsrr->rtype)
        {
            pDns->AddrArray[pDns->nIpAddrCount++] = pDnsrr->addr;
        }
    }

    if (pDns->nIpAddrCount)
    {
        pDns->pfnCallback(pDns, pDns->strHost, pDns->nIpAddrCount, ERROR_SUCCESS);
    }
    else
    {
        dnsio_read(pDns);
    }

}

void dnsio_write_cb(struct __IODGRAM * pDgram, unsigned int uSize,
    const SOCKADDR_IN * pRemote, void * pUserData)
{
    dnsio_send_query((DNSIO*)pUserData);
}

void dnsio_signal_cb(struct __IODGRAM * pDgram, DWORD dwEvent, DWORD dwError)
{
    DNSIO * pDns = (DNSIO*)iodgram_get_userdata(pDgram);
    pDns->pfnCallback(pDns, pDns->strHost, 0, dwError);
}

